#include "BZip2.h"
#include "./bz2/bzlib.h"

namespace OCPP
{
    size_t BZip2::openBZ2(std::string filename, std::ostream &os)
    {
        size_t size = 0;
        BZFILE *bzfile = BZ2_bzopen(filename.c_str(), "r");
        if (bzfile)
        {
#define BUFF_SIZE 10240
            char buffer[BUFF_SIZE];
            int read = BZ2_bzread(bzfile, buffer, BUFF_SIZE);
            while (read > 0)
            {
                size += read;
                os.write(buffer, read);
                read = BZ2_bzread(bzfile, buffer, BUFF_SIZE);
            }
            BZ2_bzclose(bzfile);
        }
        return size;
    }

    size_t BZip2::decpressBZ2(std::string &block, std::string &dest)
    {
        uint32 size = dest.size();
        BZ2_bzBuffToBuffDecompress(dest.data(), &size, block.data(), block.size(), 0, 0);
        return size;
    }

    size_t BZip2::decpressBZ2(std::vector<uint8> &block, std::string &dest)
    {
        int ret, bzerror;
        bz_stream strm = {0};
        uint32_t buffer_size = 4096;
        uint32_t total_out = 0;

        dest.resize(buffer_size);
        ret = BZ2_bzDecompressInit(&strm, 0, 0);
        if (ret != BZ_OK)
        {
            return ret;
        }
        strm.next_in = (char *)&block[0];
        strm.avail_in = block.size();

        do
        {
            if (total_out >= buffer_size)
            {
                buffer_size *= 2;
                dest.resize(buffer_size);
            }

            strm.next_out = (char *)(dest.data() + total_out);
            strm.avail_out = buffer_size - total_out;

            bzerror = BZ2_bzDecompress(&strm);
            if (bzerror != BZ_OK && bzerror != BZ_STREAM_END)
            {
                BZ2_bzDecompressEnd(&strm);
                return bzerror;
            }

            total_out = strm.total_out_lo32;
        } while (bzerror != BZ_STREAM_END);

        if (total_out > 0)
        {
            dest.resize(total_out);
        }

        BZ2_bzDecompressEnd(&strm);

        return total_out;
    }
}
